/*!
 * @file Root.cpp
 * @author Rocco Martino
 */
/***************************************************************************
 *   Copyright (C) 2015 by Rocco Martino                                   *
 *   martinorocco@gmail.com                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License as        *
 *   published by the Free Software Foundation; either version 2.1 of the  *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

/* ======================================================================= */
/* ....................................................................... */
#include <ooViewer/Root>
#include <ooViewer/SceneHandler>

#include <ooGame/SceneActuator>

#include <osgUtil/CullVisitor>

#include <osgODE/Notify>
#include <osgODE/ThreadedManager>
#include <osgODE/World>
#include <osgODE/RigidBody>

#include <osgODEUtil/FindObjects>

#include <osg/ValueObject>
/* ....................................................................... */
/* ======================================================================= */




using namespace ooViewer ;




/* ======================================================================= */
/* ....................................................................... */
Root::Root(void):
    m_dirty_children    ( true ),
    m_set_scene         ( true ),
    m_scene_name        ( "Scene" )
{
}
/* ....................................................................... */
/* ======================================================================= */




/* ======================================================================= */
/* ....................................................................... */
Root::Root(const Root& other, const osg::CopyOp& copyop):
    osg::Group          ( other, copyop ),
    m_dirty_children    ( true ),
    m_set_scene         ( other.m_set_scene ),
    m_scene_name        ( other.m_scene_name )
{
}
/* ....................................................................... */
/* ======================================================================= */




/* ======================================================================= */
/* ....................................................................... */
Root::~Root(void)
{
}
/* ....................................................................... */
/* ======================================================================= */




/* ======================================================================= */
/* ....................................................................... */
void
Root::childInserted(unsigned int /*pos*/)
{
    m_dirty_children = true ;
}
/* ....................................................................... */
/* ======================================================================= */




/* ======================================================================= */
/* ....................................................................... */
void
Root::childRemoved(unsigned int /*pos*/, unsigned int /*numChildrenToRemove*/)
{
    m_dirty_children = true ;
}
/* ....................................................................... */
/* ======================================================================= */




/* ======================================================================= */
/* ....................................................................... */
void
Root::traverse(osg::NodeVisitor& nv)
{
    if( m_dirty_children ) {

        _initChildren() ;

        if( m_scene_name != "" ) {
            requestScene( m_scene_name ) ;
        }


        m_dirty_children = false ;
    }





    if( m_set_scene ) {


        // shutdown all the managers
        for( unsigned int i=0; i<m_threaded_managers.size(); i++ ) {

            for( unsigned int j=0; j<m_threaded_managers[i].size(); j++ ) {

                m_threaded_managers[i][j]->setDone( true ) ;
            }
        }


        for( unsigned int i=0; i<m_threaded_managers.size(); i++ ) {

            for( unsigned int j=0; j<m_threaded_managers[i].size(); j++ ) {

                while( m_threaded_managers[i][j]->isRunning() ) ;
            }
        }




        int    idx = getSceneIDX( m_scene_name ) ;

        // set the scene and start the managers

        if( idx >= 0 ) {

            setScene( idx ) ;


            for( unsigned int i=0; i<m_threaded_managers[idx].size(); i++ ) {

                m_threaded_managers[idx][i]->setDone( false ) ;
            }
        }

        m_set_scene = false ;

    }






    if( ! m_current_scene.valid() ) {
        return ;
    }





    m_current_scene->accept( nv ) ;
}
/* ....................................................................... */
/* ======================================================================= */









/* ======================================================================= */
/* ....................................................................... */
namespace {
class CollectDataVisitor: public osg::NodeVisitor
{
    public:
        CollectDataVisitor( std::vector< osg::ref_ptr<osg::Node> >& scenes):
            osg::NodeVisitor    ( TRAVERSE_ALL_CHILDREN ),
            m_scenes            ( scenes ) {}


        virtual ~CollectDataVisitor(void) {}


        virtual void    apply(osg::Node& node)
        {
            bool    oo_scene = false ;

            if( node.getUserValue("oo_scene", oo_scene) ) {
                m_scenes.push_back( &node ) ;
            }


            traverse(node) ;
        }

    private:
        std::vector< osg::ref_ptr<osg::Node> >&     m_scenes ;
} ;
}
/* ....................................................................... */
/* ======================================================================= */




/* ======================================================================= */
/* ....................................................................... */
void
Root::_initChildren(void)
{
    m_scenes.clear() ;

    m_threaded_managers.clear() ;





    osg::ref_ptr<ooGame::SceneActuator::Handler>    scene_handler = new SceneHandler( this ) ;


    CollectDataVisitor  nv( m_scenes ) ;
    this->osg::Group::traverse( nv ) ;

    PS_DBG("ooViewer::Root::_initChildren(%p): collected %u scene(s)", this, (unsigned int) m_scenes.size()) ;




    for( unsigned int i=0; i<m_scenes.size(); i++ ) {

        m_threaded_managers.push_back( std::vector<osgODE::ThreadedManager*>() ) ;


        osgODEUtil::Managers    managers ;
        osgODEUtil::findManagers( m_scenes[i], managers ) ;


        PS_DBG("ooViewer::Root::_initChildren(%p): scene %u has %u manager(s)", this, i, (unsigned int) managers.size()) ;



        for( unsigned int j=0; j<managers.size(); j++ ) {


            osgODE::ThreadedManager*    threaded_manager = managers[j]->asThreadedManager() ;

            if( threaded_manager ) {
                m_threaded_managers[i].push_back( threaded_manager ) ;
            }



            osgODE::World*  world = managers[j]->getWorld() ;

            PS_ASSERT1( world ) ;

            ooGame::setSceneActuatorHandler( world, scene_handler ) ;
        }
    }
}
/* ....................................................................... */
/* ======================================================================= */




/* ======================================================================= */
/* ....................................................................... */
void
Root::requestScene( const std::string& scene_name )
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock( m_set_scene_lock ) ;

    m_scene_name = scene_name ;
    m_set_scene = true ;
}
/* ....................................................................... */
/* ======================================================================= */
